package org.c41.reflect;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.c41.util.Check;
import org.c41.util.UncheckedException;

public class ClassHelper {

	private Class<?> cl;

	public ClassHelper(Class<?> cls){
		Check.notNull(cls);

		this.cl=cls;
	}

	public ClassHelper(String clname) throws ClassNotFoundException{
		this.cl=Reflect.forName(clname);
	}

	public Class<?> getReflectClass(){
		return this.cl;
	}

	public Class<?> getArrayBasicType(){
		return this.cl.getComponentType();
	}

	public Constructor<?>[] getDeclaredConstructors(){
		return this.cl.getDeclaredConstructors();
	}

	public Constructor<?> getDeclaredConstructor(Class<?>... argstype) throws NoSuchMethodException{
		return this.cl.getDeclaredConstructor(argstype);
	}

	public Constructor<?>[] getPublicConstructors(){
		return this.cl.getConstructors();
	}

	public Constructor<?> getPublicConstructor(Class<?>... argstype) throws NoSuchMethodException{
		return this.cl.getConstructor(argstype);
	}

	public Method[] getDeclaredMethods(){
		return this.cl.getDeclaredMethods();
	}

	public Method getDeclaredMethod(String name, Class<?>...argstype) throws NoSuchMethodException{
		return this.cl.getDeclaredMethod(name, argstype);
	}

	public Method[] getPublicMethods(){
		return this.cl.getMethods();
	}

	public Method getPublicMethod(String name, Class<?>...argstype) throws NoSuchMethodException{
		return this.cl.getMethod(name, argstype);
	}

	public Field[] getDeclaredFields(){
		return this.cl.getDeclaredFields();
	}

	public Field getDeclaredField(String name) throws NoSuchFieldException{
		return this.cl.getDeclaredField(name);
	}

	public Field[] getPublicFields(){
		return this.cl.getFields();
	}

	public Field getPublicField(String name) throws NoSuchFieldException{
		return this.cl.getField(name);
	}

	public <T extends Annotation> T getAnnotaion(Class<T> annotation){
		return this.cl.getAnnotation(annotation);
	}

	public Class<?> getSuperClass(){
		return this.cl.getSuperclass();
	}

	public Annotation[] getAnnotations(){
		return this.cl.getAnnotations();
	}

	public Annotation[] getDeclaredAnnotations(){
		return this.cl.getDeclaredAnnotations();
	}

	public Object newInstance(Class<?>[] argstype, Object[] args) throws NoSuchMethodException, InstantiationException, IllegalArgumentException, InvocationTargetException{
		try {
			Constructor<?> cons = this.cl.getConstructor(argstype);
			cons.setAccessible(true);
			return cons.newInstance(argstype, args);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public Object newInstancef(Class<?>[] argstype, Object[] args) throws NoSuchMethodException, InstantiationException, IllegalArgumentException, InvocationTargetException{
		return Reflect.newInstance(this.cl, argstype, args);
	}

	public Object getField(Object obj, String field) throws NoSuchFieldException, IllegalArgumentException{
		try {
			Field f = cl.getField(field);
			f.setAccessible(true);
			return f.get(obj);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public Object getFieldf(Object obj, String field) throws NoSuchFieldException, IllegalArgumentException{
		try {
			Field f = cl.getDeclaredField(field);
			f.setAccessible(true);
			return f.get(obj);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public Object getStaticField(String field) throws NoSuchFieldException, IllegalArgumentException{
		return getField(null, field);
	}

	public Object getStaticFieldf(String field) throws NoSuchFieldException, IllegalArgumentException{
		return getFieldf(null, field);
	}

	public void setField(Object obj, String field, Object value) throws NoSuchFieldException, IllegalArgumentException{
		try {
			Field f = cl.getField(field);
			f.setAccessible(true);
			f.set(obj, value);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public void setFieldf(Object obj, String field, Object value) throws NoSuchFieldException, IllegalArgumentException{
		try {
			Field f = cl.getDeclaredField(field);
			f.setAccessible(true);
			f.set(obj, value);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public void setStaticField(String field, Object value) throws NoSuchFieldException, IllegalArgumentException{
		setField(null, field, value);
	}

	public void setStaticFieldf(String field, Object value) throws NoSuchFieldException, IllegalArgumentException{
		setFieldf(null, field, value);
	}

	public Object invoke(Object obj, String method, Class<?>[] argstype, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException{
		try {
			Method m = cl.getMethod(method, argstype);
			m.setAccessible(true);
			return m.invoke(obj, args);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public Object invokef(Object obj, String method, Class<?>[] argstype, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException{
		try {
			Method m = cl.getDeclaredMethod(method, argstype);
			m.setAccessible(true);
			return m.invoke(obj, args);
		} catch (IllegalAccessException e) {
			throw new UncheckedException(e);
		}
	}

	public Object invokeStatic(String method, Class<?>[] argstype, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException{
		return invoke(null, method, argstype, args);
	}

	public Object invokeStaticf(String method, Class<?>[] argstype, Object[] args) throws NoSuchMethodException, IllegalArgumentException, InvocationTargetException{
		return invokef(null, method, argstype, args);
	}

	public boolean isAnnotation(){
		return this.cl.isAnnotation();
	}

	public boolean isArray(){
		return this.cl.isArray();
	}

	public boolean isEnum(){
		return this.cl.isEnum();
	}

	public boolean isInterface(){
		return this.cl.isInterface();
	}

	public boolean isAbstract(){
		return Modifiers.isAbstract(this.cl);
	}

	public boolean isAnonymousClass(){
		return this.cl.isAnonymousClass();
	}

	public boolean isAssignableFrom(Class<?> cls){
		return this.cl.isAssignableFrom(cls);
	}

	public boolean instance(Object obj){
		return this.cl.isInstance(obj);
	}

	public boolean isLocalClass(){
		return this.cl.isLocalClass();
	}
	
	public boolean isMemberClass(){
		return this.cl.isMemberClass();
	}
	
	public boolean isPrimitive(){
		return this.cl.isPrimitive();
	}
	
	public boolean isSynthetic(){
		return this.cl.isSynthetic();
	}

	public GenericDeclaration getEnclosing(){
		GenericDeclaration result = null;
		if(result == null){
			result = this.cl.getEnclosingClass();
		}
		if(result == null){
			result = this.cl.getEnclosingConstructor();
		}
		if(result == null){
			result = this.cl.getEnclosingMethod();
		}
		return result;
	}

	public Class<?>[] getInterfaces(){
		return this.cl.getInterfaces();
	}

	public String getName(){
		return Modifiers.castToNormalName(this.cl.getName());
	}

	public Package getPackage(){
		return this.cl.getPackage();
	}

	@Override
	public String toString(){
		return this.cl.getName();
	}
	
}
